/**
 * HW2 Part 2 - Coding with bitwise operators
 * 
 * (Note the rules are different than in the other java file).
 * The only operators you are allowed to use in your code are the following
 *	- The bitwise operators |, &, ~, ^, <<, >>
 *	- Increment and Decrement ++, and --.  You may also use add things with 1 or subtract things with 1.
 *	- Boolean operators ||, &&, and ! only in if statements
 *	- Conditional statements (if, if-else, switch-case, ? :).
 *	- Equality comparisons (==, !=).
 *
 * Anything not in the above list you are not allowed to use. This includes the following but is not an exhaustive list:
 *	- Multiplication & Division
 *	- Addition & Subtraction with numbers other than 1.
 *	- Modulus (%)
 *	- Iteration (for, while, do-while loops, recursion)
 *  - The unsigned right shift operator >>> (C does not provide this operator).
 *	- Any functions found in Java libraries (especially the Math library)
 *		- Example Math.pow, Integer.bitCount, etc.
 *	- Greater than and less than comparisons (>, <, >=, <=)
 *	- Casting (you should not have cast anywhere!)
 *
 * As a note all of these functions accept ints. Here is the reason
 * 1) If you pass in a number (which is of type int by default) into a function accepting a byte
 *    the Java compiler will complain even if the number would fit into that type.
 *    
 * Now keep in mind the return value is also an int.  Please read the comments on how many bits
 * to return and make sure the other bits are not set or else you will not get any points for that
 * test case. i.e if I say to return 6 bits and you return 0xFFFFFFFF you will lose points.
 *
 * Definitions of types
 * nibble (nybble) - 4 bits
 * byte - 8 bits
 * short - 16 bits
 * int - 32 bits
 * 
 * @author Kyle Kelly
 * 
 */
public class HW2Operations
{
	
	public static void main(String[] args)
	{
		// Test your code here.
		// You should devise your own test cases here in addition to the ones in the comments.
		// Come up with one more test case than the ones given in the comments.
		System.out.printf("setByte(0x1357, 0x24, 0) expected: 0x2457, result: 0x%x\n", setByte(0x1357, 0x24, 1));
		System.out.printf("getByte(0x188874, 2) expected: 0x18, result: 0x%x\n", getByte(0x188874, 2));
		System.out.printf("pack(0xFEED, 0xAABE) expected: 0xFEEDAABE, result: 0x%x\n", pack(0xFEED, 0xAABE));
		int[] r = unpack(0xFEEDAABE);
		System.out.printf("unpack(0xFEEDAABE) expected: 0xF, 0xE, 0xE, 0xD, 0xA, 0xA, 0xB, 0xE, result: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n",
				r[0], r[1], r[2], r[3], r[4],r[5],r[6],r[7]);
		System.out.printf("negate(0xE2) expected: 0x1E, result: 0x%x\n", negate(0xE2,8));
		System.out.printf("signExtend(0x7BC,11,32) expected: 0xFFFFFFBC , result: 0x%x\n", signExtend(0x7BC, 11, 32));
	}
	
	/**
	 * Sets a byte in a integer
	 * 
	 * Integers are made of four bytes, numbered like so: 33333333 22222222 11111111 00000000
	 *
	 * For a graphical representation of this:
	 *    3                   2                   1                 
	 *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 * |     Byte3     |     Byte2     |     Byte1     |     Byte0     |
	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 * 
	 * 
	 * Examples:
	 *      setByte(0x25093456, 0x18, 0) //=> 0x25093418
	 *      setByte(0x12374925, 0xF, 1)  //=> 0x12370F25
	 * 
	 * @param num integer that will be modified
	 * @param b byte to insert into the integer
	 * @param which determines which byte gets modified - 0 for least significant byte
	 *            
	 * @return the modified integer
	 */
	public static int setByte(int num, int b, int which)
	{
		num = num & ~(0xFF<<(which<<0x3));
		num = num | b<<(which<<0x3);
		return num;
	}
	
	/**
	 * Gets a byte from an integer
	 * 
	 * Other examples: i.e. 
	 * getByte(0x12345678, 0) //=> 0x78
	 * getByte(0xA55AFF25, 1) //=> 0xFF
	 * 
	 * @param num an integer
	 * @param which Determines which byte gets returned - 0 for least significant byte
	 *            
	 * @return a byte corresponding 
	 */
	public static int getByte(int num, int which)
	{
		num = num & 0xFF<<(which<<0x3);
		num = num>>(which<<0x3);
		return num;
	}

	/**
	 * Packs 2 shorts (16 bits) into an int (32 bits)
	 * 
	 * The shorts should be placed consecutively in the integer in the order
	 * specified in the parameters.
	 * 
	 * i.e. pack(0x3468, 0x7723) //=> 0x34687723 
	 *      pack(0xBAAD, 0xBEEF) //=> 0xBAADBEEF
	 * 
	 * @param c1 most significant short (will always be an 16 bit number)
	 * @param c2 2nd short (will always be an 16 bit number)
	 *            
	 * @return an int formatted like so c1c2
	 */
	public static int pack(int c1, int c2)
	{
		c1 = c1<<0x10;
		c1 = c1|c2;
		return c1;
	}
	
	/**
	 * Unpacks an integer into 8 nibbles (4 bits) most significant nibble first.
	 * 
	 * i.e unpack(0x12345678) //=> {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}
	 *  
	 * @param num integer that needs to be unpacked
	 * @return an array with each nibble of the integer most significant nibble first.
	 */
	public static int[] unpack(int num)
	{
		int[] answer = {0, 0, 0, 0, 0, 0, 0, 0};
		answer[0] = num>>0x1C & 0xF;
		answer[1] = (num>>0x18) & 0xF;
		answer[2] = (num>>0x14) & 0xF;
		answer[3] = (num>>0x10) & 0xF;
		answer[4] = (num>>0xC) & 0xF;
		answer[5] = (num>>0x8) & 0xF;
		answer[6] = (num>>0x4) & 0xF;
		answer[7] = num & 0xF;
		return answer;
	}

	/**
	 * Negates an 'x' bit number.
	 * 
	 * Examples:
	 *      negate(0x000001, 23) //=> 0x7FFFFF
	 *      negate(0x7FFFFF, 23) //=> 0x000001
	 *      negate(0x000000, 24) //=> 0x000000
	 *      negate(0x1, 2) //=> 0x3 
	 * 
	 * @param num 2's complement 'x' bit number to negate.
	 * @param 'x' number of bits num is
	 * @return the negated version an 'x' bit binary number.
	 */
	public static int negate(int num, int x)
	{
		num = ( (~num) ^ ((~0)<<x));
		return ++num;
	}

	/**
	 * Sign extends an 'x' bit value to a 'y' bit value
	 * 
	 * Both the parameter and return value are ints, but the parameter only uses the least significant
	 * x bits, and the return value only uses the least significant y bits (the rest are zeros)
	 * 
	 * Examples:
	 *  signExtend(0x000000D2, 9, 32) results in 0x000000D2
	 * 	signExtend(0x000007E4, 11, 32) results in 0xFFFFFFE4
	 * 
	 * @param num an x bit 2's complement number
	 * @param x the original bit length
	 * @param y the new bit length
	 * @warning You will be guaranteed y > x.  If this precondition does not hold results are undefined.
	 * @return a 'y' bit 2's complement number, the sign extended value of num.
	 */
	public static int signExtend(int num, int x, int y)
	{
		int sign = num>>--x;
		if(sign == 1){
			int ones = (~0)>>(y-x);
			return num | (ones<<x);
		}
		return num;
	}
}
